/**
 * \file glsl.cpp
 * GLSL Shader Support
 * 
 * \sa glsl.h
 **
 * Author: Mac Duy Hai
 **
 * License... etc.
 */

#include "3d.h"
#include "glsl.h"
#include "render.h"
#include <fstream>
#include <string>

using namespace WtfEngine;

//
// GLSLShader
//

GLSLShader::GLSLShader() {
	
};

GLSLShader::~GLSLShader() {
	
};


void GLSLShader::Load(String sFilePath) {
	using namespace std;
	string				type;
	
	//First, let us load the vertex shader.
	std::fstream shaderFile(sFilePath.c_str(), std::ios::in);
	std::string sShader;
	const char * szShader;
	 
	if (shaderFile.is_open())
	{
		std::stringstream buffer;
		buffer << shaderFile.rdbuf();
		sShader = buffer.str();
	} else {
		throw "Failed to load shader";
	};
	
	// determine shader type & compile
	type = sFilePath.substr( sFilePath.length() - 5, 5);
	if (type.compare(".vert") == 0) {
		hndShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	} else if (type.compare(".geom") == 0) {
		hndShader = glCreateShaderObjectARB(GL_GEOMETRY_SHADER_EXT);
	} else {
		hndShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	}

	szShader = sShader.c_str();
	glShaderSourceARB(hndShader, 1, (const GLcharARB**) &szShader, NULL);
	glCompileShaderARB(hndShader);
	
	// check result
	int compiled = 0, length = 0, laux = 0;
	glGetObjectParameterivARB(hndShader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);
	glGetObjectParameterivARB(hndShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
	GLcharARB *logString = new GLcharARB[length];
	glGetInfoLogARB(hndShader, length, &laux, logString);

	// Report result
	if (compiled) {
		Log(LOG_INFO, "Shader successfuly compiled");
	} else {
		Log(LOG_ERROR, String("Shader error: ") + sFilePath );
		Log(LOG_ERROR, logString);
	}

	delete logString;
};

//
// GLSL Program
//

GLSLProgram::GLSLProgram() {
	
};

GLSLProgram::~GLSLProgram() {
	
};

void GLSLProgram::Load(GLSLShader::tRef glslVertShader, GLSLShader::tRef glslFragShader) {	
	// Store references
	this->VertexShader = glslVertShader;
	this->FragmentShader = glslFragShader;
	
	// Create object and insert shaders
	hndProgram = glCreateProgramObjectARB();
	glAttachObjectARB(hndProgram, glslVertShader->getHandle());
	
	glAttachObjectARB(hndProgram, glslFragShader->getHandle());
	
	// Link and check errors
	glLinkProgramARB(hndProgram);
	int result = 0, length = 0, laux = 0;
	glGetObjectParameterivARB(hndProgram,GL_OBJECT_LINK_STATUS_ARB, &result);
	glGetObjectParameterivARB(hndProgram,GL_OBJECT_INFO_LOG_LENGTH_ARB,&length);
	GLcharARB *infoLog = (GLcharARB *)malloc(length * sizeof(GLcharARB));
	glGetInfoLogARB(hndProgram,length,&laux,infoLog);
	
	if (result) {
		Log(LOG_INFO, "Shader Program successfully linked");
		GetLocations();
	} else {
		Log(LOG_ERROR, "Shader Program linking error:");
		Log(LOG_ERROR, infoLog);
	}

	free(infoLog);
};

/**********************************
 * Shader Program Classes		  *
 **********************************/
 
using namespace WtfGraphics;

using namespace WtfEngine::ShaderPrograms;
void SpecularShader::PassMesh(Mesh* mesh) {
	//mesh->PassTexture(hndTexture, 0);
};

void SpecularShader::PassOther() {
	glUniform3fARB(hndCamPosition, 	(GLfloat) Renderer::GetInstance().GetCamera()->getPosition().x(),
   							   		(GLfloat) Renderer::GetInstance().GetCamera()->getPosition().y(),
									(GLfloat) Renderer::GetInstance().GetCamera()->getPosition().z());
};


void NormalShader::PassMesh(Mesh* mesh) {
	SpecularShader::PassMesh(mesh);
	
	//mesh->PassTexture(hndNormalTexture, 1);
	//mesh->PassTexture(hndHeightTexture, 2);
	
	// pass tangents
	hndTangent 		 = glGetAttribLocationARB(hndProgram, "tangent");
	// glEnableVertexAttribArrayARB(hndTangent);
	//mesh->PassTangents(hndTangent);
}

void NormalShader::Deactivate() {
	// glDisableVertexAttribArrayARB(hndTangent);
}